﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Content;


namespace MiaProject
{
    class Player
    {
        private static Player instance;
        private bool key_tiro;
        public int powerAtack;
        public int powerShield;        
        public Model model;                             //modelo do player
        public Model tiro;                              //modelo do tiro
        //public SoundEffect soundTiro;                   //som do disparo do tiro
        //public SoundEffect soundTiroCollision;          //som da colisao do tiro com o player
        public int typeTiro;
        public Vector3 pos;                             //posicao 
        public float rotation;                          //rotacao realizada com controles remotos
        public float rotationStart;                     //rotacao inical para ajuste do modelo
        public float scale;                             //escala
        public Vector3 vel;                             //velocidade
        public List<Tiro> tiros;                        //Lista de tiros
        public int velTiro;

        //private float timeCountStart;                   //tempo que fica fora de alguma plataforma
        public int lastRoom;                           //index da ultima room em LevelManager que foi detectado que o player estava (para otimizacao)
        public float life;
        public const int NR_TIROS = 100;
        private float timeCountTiro;

        public static Player getInstance()
        {
            if (instance==null)
                instance = new Player();
            return instance;
        }
             

        public Player()
        {
            this.pos            = Vector3.Zero;
            this.vel            = Vector3.Zero;
            this.rotation       = 0.0f;
            this.rotationStart  = 0.0f;
            this.scale          = 120.0f;
            this.tiros          = new List<Tiro>();
            this.key_tiro       = false;
            this.powerAtack     = 10;
            this.powerShield    = 100;
            //this.timeCountStart = 0;
            this.lastRoom       = 0;
            this.life = 1.0f;
            this.typeTiro = 1;
            this.timeCountTiro = 0;
            this.velTiro = 1100;
        }
        public void load(ContentManager content)
        {
            this.model = content.Load<Model>(".\\model\\player\\ship01");
            this.tiro = content.Load<Model>(".\\model\\tiro\\tiro01");
            //this.soundTiro = content.Load<SoundEffect>(".\\sound\\tiro\\sound_tiro01");
            //this.soundTiroCollision = content.Load<SoundEffect>(".\\sound\\tiro\\sound_tiro_collision01");

            this.scale = 0.1f;
            this.pos.Y = 40;
            
        }
        public void setAttack(float attack)
        {
            this.life -= attack;
            if (this.life < 0)
                this.life = 0;
        }
        public void update(KeyboardState ks, GameTime gameTime)
        {
            this.pos += this.vel;
            float velP = 53;
            if (ks.IsKeyDown(Keys.Space))
                velP = 40;
            
            //~~~~~~~ verificar se está em alguma room do mapa ~~~~~~~~~~~~ 
            
            LevelManager lm = LevelManager.getInstance();
            Sound s = Sound.getInstance();
            lastRoom = lm.inRoom(this.pos,lastRoom);
            
            /*Com contador de tempo
            if(lastRoom == -1)//nao está em nenhuma
            {
                
                if (timeCountStart == 0)//inicia o contador
                    timeCountStart = (float)gameTime.TotalGameTime.TotalSeconds;
                else
                {
                    if (gameTime.TotalGameTime.TotalSeconds - timeCountStart > 0)//1 segundos fora
                        velP = 30;
                }
                
            }
            else if(timeCountStart > 0)
                timeCountStart = 0;
            */
            //sem contador de tempo
            if (lastRoom == -1)//nao está em nenhuma
                velP = 30;
            //~~~~~~~~~~ 

            if (ks.IsKeyDown(Keys.Left))
                this.rotation += (float)(3 * gameTime.ElapsedGameTime.TotalSeconds);//0.05f;
            if (ks.IsKeyDown(Keys.Right))
                this.rotation -= (float)(3 * gameTime.ElapsedGameTime.TotalSeconds);

            
            
            Vector3 modelVelAdd = Vector3.Zero;
            modelVelAdd.X = -(float)Math.Sin(this.rotation);
            modelVelAdd.Z = -(float)Math.Cos(this.rotation);
            this.vel += modelVelAdd;
            this.vel *= (float)(velP * gameTime.ElapsedGameTime.TotalSeconds);//fazer como gametime
        
            
            //atira quando tecla pressionada a cada 0.2 segundos e quando tecla é solta.
            Boolean shot = false;
            
            //Verifica se a tecla de tiro foi pressionada e seta o tiro
            if (ks.IsKeyDown(Keys.S))
            {
                if (!key_tiro)
                    timeCountTiro = (float)gameTime.TotalGameTime.TotalSeconds;
                key_tiro = true;

                if ((float)gameTime.TotalGameTime.TotalSeconds - timeCountTiro > 0.2)
                {
                    shot = true;
                    timeCountTiro = (float)gameTime.TotalGameTime.TotalSeconds;
                }

            }
            else if (ks.IsKeyUp(Keys.S) && key_tiro)
            {
                key_tiro = false;
                shot = true;
            }
            
            if(shot)
            {
                switch (typeTiro)
                {
                    case 1:
                        setTiro();
                        break;
                    case 2:
                        setTiro2();
                        break;
                    case 3:
                        setTiro3();
                        break;
                    case 4:
                        setTiro();
                        setTiro2();
                        break;
                    case 5:
                        setTiro();
                        setTiro3();
                        break;
                }
                //soundTiro.Play();
                s.soundBank.PlayCue("player_shot");
            }

            moveTiro(gameTime);
            deleteTiro();
        }//end of method
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
        public void addLife(int life)
        {
            this.life += (float)(0.01 * life);
            if (this.life > 1)
                this.life = 1;
        }
        private void setTiro()
        {
            //TIRO 1
            Tiro t = new Tiro();

            Vector3 dir = Vector3.Zero;
            dir.X = -(float)Math.Sin(this.rotation);
            dir.Y = -1;
            dir.Z = -(float)Math.Cos(this.rotation);
            t.yMin = -50;
            t.startTiro(this.pos, dir, this.rotation + 3.0f,this.velTiro);

            tiros.Add(t);
            

        }//end method
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

        private void setTiro2()
        {
            //TIRO 1
            Tiro t = new Tiro();

            Vector3 dir = Vector3.Zero;
            dir.X = -(float)Math.Sin(this.rotation + 0.1f);
            dir.Y = -1;
            dir.Z = -(float)Math.Cos(this.rotation + 0.1f);
            t.yMin = -50;
            t.startTiro(this.pos, dir, this.rotation + 3.0f + 0.1f, this.velTiro);

            tiros.Add(t);

            //TIRO 2
            t = new Tiro();

            dir = Vector3.Zero;
            dir.X = -(float)Math.Sin(this.rotation - 0.1f);
            dir.Y = -1;
            dir.Z = -(float)Math.Cos(this.rotation - 0.1f);
            t.yMin = -50;
            t.startTiro(this.pos, dir, this.rotation + 3.0f - 0.1f, this.velTiro);

            tiros.Add(t);

        }//end method
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

        private void setTiro3()
        {
            //TIRO 1
            Tiro t = new Tiro();
            
            Vector3 dir = Vector3.Zero;
            dir.X = -(float)Math.Sin(this.rotation);
            dir.Y = -1;
            dir.Z = -(float)Math.Cos(this.rotation);

            t.startTiro(this.pos, dir, this.rotation + 3.0f, this.velTiro);
            t.yMin = -50;
            tiros.Add(t);

            //TIRO 2
            t = new Tiro();

            dir = Vector3.Zero;
            dir.X = -(float)Math.Sin(this.rotation + 0.5f);
            dir.Y = -1;
            dir.Z = -(float)Math.Cos(this.rotation + 0.5f);
            t.yMin = -50;
            t.startTiro(this.pos, dir, this.rotation + 3.0f + 0.5f, this.velTiro);

            tiros.Add(t);
            
            //TIRO 3
            t = new Tiro();

            dir = Vector3.Zero;
            dir.X = -(float)Math.Sin(this.rotation - 0.5f);
            dir.Y = -1;
            dir.Z = -(float)Math.Cos(this.rotation - 0.5f);
            t.yMin = -50;
            t.startTiro(this.pos, dir, this.rotation + 3.0f - 0.5f, this.velTiro);
            
            tiros.Add(t);

              
        }//end method
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

        private void moveTiro(GameTime gameTime)
        {
            foreach (Tiro t in tiros)
                t.move((float)gameTime.ElapsedGameTime.TotalSeconds);                
            
        }
        private void deleteTiro()
        {
            List<Tiro> deleteTiro = new List<Tiro>();
            foreach (Tiro t in tiros)
            {
                if (!t.status)
                    deleteTiro.Add(t);
            }
            foreach (Tiro t in deleteTiro)
                tiros.Remove(t);
        }

        public void draw(Matrix view, Matrix projection)
        {
            Matrix[] transforms = new Matrix[this.model.Bones.Count];
            this.model.CopyAbsoluteBoneTransformsTo(transforms);

            /*
             Desenhar cada um dos meshes do modelo e aplicar os efeitos
             * Cada modelo pode ter mais de uma mesh e a cada mesh podem ser aplicados 1 ou mais efeitos;

             */
            foreach (ModelMesh mesh in this.model.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.EnableDefaultLighting();//habilita luz

                    //Posição do modelo no mundo;Coloquei uma rotação em Y, para fazer o modelo girar em relação a este eixo;
                    effect.World = transforms[mesh.ParentBone.Index] * Matrix.CreateRotationY(this.rotationStart+this.rotation) * Matrix.CreateScale(this.scale) * Matrix.CreateTranslation(this.pos);

                    //Posição e direção da câmera;
                    effect.View = view;

                    //Controla como o mundo 3D é projetado de maneira 2D;
                    effect.Projection = projection;

                }
                mesh.Draw();
            }

            drawTiro(view, projection);

        }//end of method
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
        private void drawTiro(Matrix view, Matrix projection)
        {
            Matrix[] transformsTiro;

            foreach (Tiro t in this.tiros)
            {
                if (!t.status)
                    continue;


                transformsTiro = new Matrix[this.tiro.Bones.Count];
                this.tiro.CopyAbsoluteBoneTransformsTo(transformsTiro);

                foreach (ModelMesh mesh in this.tiro.Meshes)
                {
                    foreach (BasicEffect effect in mesh.Effects)
                    {
                        effect.EnableDefaultLighting();//habilita luz

                        //Posição do modelo no mundo;Coloquei uma rotação em Y, para fazer o modelo girar em relação a este eixo;
                        effect.World = transformsTiro[mesh.ParentBone.Index] * Matrix.CreateScale(4.0f) * Matrix.CreateRotationY(t.modelRot) * Matrix.CreateTranslation(t.pos);

                        //Posição e direção da câmera;
                        effect.View = view;

                        //Controla como o mundo 3D é projetado de maneira 2D;
                        effect.Projection = projection;

                    }
                    mesh.Draw();
                }

            }
        }//end of method
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

    }
}
